package deepbrain.simnetwork.network;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import deepbrain.simnetwork.mechanism.DummyMechanism;
import deepbrain.simnetwork.mechanism.Mechanism;
import deepbrain.simnetwork.structure.Connection;

/**
 * The Network class represents a network constructed by nodes.
 * 
 * A Network in SimNetwork has two aspects: nodes, connections.
 * 
 * Now, the Network only support fixed node number. In other words, the Network
 * cannot be add new nodes.
 * 
 * Network should have a mechanism, and each node can have its own mechanism. If
 * one node's mechanism is null, use network's mechanism instead.
 * 
 * Note that a network is only a model describing a structural model, it doesn't
 * have certain state, so two simulating task can share the same network without
 * interference.
 * 
 * @author Li Yang
 * @create 2009-2-23
 */
public class Network implements Serializable, Cloneable {

	private static final long serialVersionUID = -6470409820364133857L;

	protected String name;

	protected int numOfNodes;

	protected Set<Connection<?>> connections;

	protected Set<Connection<?>>[] aConnections;
	protected Set<Connection<?>>[] eConnections;

	/**
	 * The Mechanism of Network
	 */
	protected Mechanism mechanism;
	/**
	 * Each node's own mechanism
	 */
	protected Mechanism[] mechanisms;

	public Network(int numOfNodes) {
		this.numOfNodes = numOfNodes;
		mechanisms = new Mechanism[numOfNodes];
	}

	public Network(int numOfNodes, Set<Connection<?>> connections) {
		this.numOfNodes = numOfNodes;
		mechanisms = new Mechanism[numOfNodes];

		this.connections = connections;
		buildUpConnections(connections);
	}

	public Network(int numOfNodes, Set<Connection<?>> connections,
			Mechanism[] mechanisms) {
		this(numOfNodes);
		this.mechanisms = mechanisms;

		this.connections = connections;
		buildUpConnections(connections);
	}

	@SuppressWarnings("unchecked")
	protected void buildUpConnections(Set<Connection<?>> connections) {
		aConnections = new HashSet[numOfNodes];
		eConnections = new HashSet[numOfNodes];
		for (int i = 0; i < numOfNodes; i++) {
			aConnections[i] = new HashSet<Connection<?>>();
			eConnections[i] = new HashSet<Connection<?>>();
		}
		for (Connection<?> connection : connections) {
			buildUpConnection(connection);
		}
	}

	protected void buildUpConnection(Connection<?> connection) {
		aConnections[connection.destination].add(connection);
		eConnections[connection.source].add(connection);
	}

	protected void tearDownConnection(Connection<?> connection) {
		aConnections[connection.destination].remove(connection);
		eConnections[connection.source].remove(connection);
	}

	public void addConnection(Connection<?> connection) {
		connections.add(connection);
		buildUpConnection(connection);
	}

	public void removeConnection(Connection<?> connection) {
		connections.remove(connection);
		tearDownConnection(connection);
	}

	public void setMechanism(Mechanism mechanism) {
		this.mechanism = mechanism;
	}

	public Mechanism getMechanism() {
		if (mechanism == null)
			mechanism = new DummyMechanism();
		return mechanism;
	}

	public Mechanism[] getMechanisms() {
		return mechanisms;
	}

	public Mechanism getMechanism(int node) {
		return mechanisms[node];
	}

	public Set<Connection<?>> getAfferentConnections(int node) {
		return aConnections[node];
	}

	public Set<Connection<?>> getEfferentConnections(int node) {
		return eConnections[node];
	}

	/**
	 * Note that the NetworkState here is a probability distributon rather than
	 * a certain network state.
	 */
	public NetworkState nextNetworkState(NetworkState state) {
		return getMechanism().process(this, state, null);
	}

	public NetworkState nextNetworkState(NetworkState state, List<Integer> part) {
		return getMechanism().process(this, state, part);
	}

	public String getName() {
		if (name == null)
			return "Network@" + hashCode();
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getNumOfNodes() {
		return numOfNodes;
	}

	public Set<Connection<?>> getConnections() {
		return connections;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder(150);
		builder.append("Network  [ ");
		builder.append(getName());
		builder.append(" ]:\n");
		builder.append("Num of Nodes: " + numOfNodes + "\n");
		builder.append("Connections: \n");
		for (Connection<?> connection : connections) {
			builder.append(connection);
			builder.append("\n");
		}
		return builder.toString();
	}

	public String toStringBrief() {
		StringBuilder builder = new StringBuilder(150);
		builder.append("Network  [ ");
		builder.append(getName());
		builder.append(" ]");
		return builder.toString();
	}

	public void setMechanisms(Mechanism[] mechanisms) {
		this.mechanisms = mechanisms;
	}
	
	public void removeAllConnections(){
		connections.clear();
		for (int i = 0; i < numOfNodes; i++) {
			aConnections[i].clear();
			eConnections[i].clear();
		}
	}

}
